import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:share_fit/app/controllers/base/base_controller.dart';
import 'package:share_fit/app/controllers/home/home_const.dart';
import 'package:share_fit/app/controllers/home/weather/model/daily_weather_model.dart';
import 'package:share_fit/app/controllers/home/weather/model/future_weather_model.dart';
import 'package:share_fit/app/controllers/home/weather/model/hourly_weather_model.dart';
import 'package:share_fit/app/controllers/home/weather/model/weather_type.dart';
import 'package:share_fit/app/helper/ext/tr_ext.dart';
import 'package:share_fit/app/network/net_const.dart';
import 'package:share_fit/app/routes/home_routes.dart';
import 'package:share_fit/app/routes/login_routes.dart';
import 'package:share_fit/app/tools/date_tool.dart';
import 'package:share_fit/app/tools/log_utils.dart';
import 'package:share_fit/app/ute/uteped_const.dart';
import 'package:share_fit/app/ute/uteped_device.dart';
import 'package:share_fit/generated/locales.g.dart';
import 'state.dart';

class WeatherLogic extends BaseController {
  final WeatherState state = WeatherState();

  final String methodSetTodayWeather = "setTodayWeather";

  goBack() {
    Get.back();
  }

  goLocationSwitch() {
    Get.toNamed(HomeRoutes.weatherLocationPage);
  }

  getTempUnit() {
    return read("space_unit_temp", defaultValue: 0, global: true);
  }

  Future<bool> loadWeatherData() async {
    try {
      // if (getToken().isEmpty) {
      //   return Future.delayed(const Duration(seconds: 2), () {
      //     showTips('', LocaleKeys.weather_not_login_tip.ts);
      //     return false;
      //   });
      // }
      await getCurrentLocation();
      DateTime start = DateTime.now();
      await getFutureWeather();
      await getTodayWeather();
      await get24HourWeather();
      logger2.d('get weather data use ${DateTime.now().millisecondsSinceEpoch - start.millisecondsSinceEpoch} ms');
      _doSyncWeatherData();
    } on TimeoutException catch (e) {
      logger2.d('get location error $e');
      showTips('', LocaleKeys.weather_location_fail_msg.ts);
    } on DioException catch (e) {
      logger2.d('parse weather data $e');
      if (e.response?.statusCode == 403) {
        showTips('', LocaleKeys.weather_not_login_tip.ts);
        Get.offNamed(LoginRoutes.loginPage, arguments: {'toMainPage': true});
      }
    } catch (e) {
      logger2.d('other error $e');
      return Future.value(false);
    }
    return Future.value(true);
  }

  Future<void> loadWeatherDataForSync() async {
    try {
      // if (getToken().isEmpty) {
      //   return Future.delayed(const Duration(seconds: 2), () {
      //     return;
      //   });
      // }
      await getCurrentLocation();
      DateTime start = DateTime.now();
      await getFutureWeather();
      // await getTodayWeather();
      await get24HourWeather();
      logger2.d('get weather data use ${DateTime.now().millisecondsSinceEpoch - start.millisecondsSinceEpoch} ms');
      _doSyncWeatherData();
    } catch (e) {
      logger2.d('parse weather data $e');
    }
  }

  _init24HWeatherList(weather24HJson) {
    state.weather24h.clear();

    if (weather24HJson['code'] == 200 && weather24HJson['data'] != null) {
      Map map = weather24HJson['data'];
      String date = map['day'];

      List times = map['times'];
      List temps = map['temps'];
      List mains = map['mains'];
      List descs = map['descriptions'];
      List pops = map['pops'];
      for (var i = 0; i < times.length; i++) {
        HourlyWeatherModel hwm = HourlyWeatherModel(
          date: DateTime.parse('$date ${DateTool.zeroFill(int.parse(times[i]))}:00:00'),
          temp: temps[i].toInt(),
          tempUnit: getTempUnit(),
          text: descs[i].toString(),
          pop: pops[i].toInt(),
        );
        state.weather24h.add(hwm);
      }
    }
  }

  getCacheKey(String prefix) {
    return '${prefix}_${state.position.longitude.toStringAsFixed(5)}_${state.position.latitude.toStringAsFixed(5)}_${DateTime.now().millisecondsSinceEpoch}';
  }

  /// 快速查询和缓解后台接口压力
  saveToCache(String rootKey, dynamic maps) {
    String key = getCacheKey("weather_${rootKey.split('_')[1]}");
    save(rootKey, key, global: true);
    save(key, maps, global: true);
  }

  _initFutureWeatherList(weatherJson) {
    state.dailyWeathers.clear();
    if (weatherJson['code'] == 200 && weatherJson['data'].length > 0) {
      List maps = weatherJson['data'];
      for (var map in maps) {
        DailyWeatherModel model = DailyWeatherModel();
        if (model.from(map)) {
          model.tempUnit = getTemperatureUnit();
          if (DateTool.isSameDay(model.date, DateTime.now())) {
            state.todayWeather = model;
          }
          state.dailyWeathers.add(model);
        }
      }
    }
  }

  _initTodayWeather(weatherJson) {
    if (weatherJson['code'] == 200 && weatherJson != null) {
      Map map = weatherJson['data'];

      DailyWeatherModel model = DailyWeatherModel();
      model.tempUnit = getTemperatureUnit();
      model.tempMin = map['main']['temp'].toInt();
      model.humidity = map['main']['humidity'].toDouble();
      model.weatherDesc = map['weather'].last['description'];
      model.date = DateTool.getDateTimeFrom(map['dt']);
      state.currentWeather = model;
    }
  }

  /// 获取温度单位：0 -> 摄氏度，1 -> 华氏度，默认0 摄氏度
  int getTemperatureUnit() {
    return read("space_unit_temp", defaultValue: 0, global: true);
  }

  Future<void> getCurrentLocation() async {
    DateTime start = DateTime.now();
    bool serviceEnabled;
    LocationPermission permission;

    // Test if location services are enabled.
    serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      // Location services are not enabled don't continue
      // accessing the position and request users of the
      // App to enable the location services.
      // return Future.error('Location services are disabled.');
      await Geolocator.openLocationSettings();
    }

    permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        // Permissions are denied, next time you could try
        // requesting permissions again (this is also where
        // Android's shouldShowRequestPermissionRationale
        // returned true. According to Android guidelines
        // your App should show an explanatory UI now.
        // return Future.error('Location permissions are denied');
        await Geolocator.openLocationSettings();
      }
    }

    if (permission == LocationPermission.deniedForever) {
      // Permissions are denied forever, handle appropriately.
      // return Future.error('Location permissions are permanently denied, we cannot request permissions.');
      await Geolocator.openAppSettings();
    }

    // When we reach here, permissions are granted and we can
    // continue accessing the position of the device.
    var position = await Geolocator.getLastKnownPosition();
    if (position != null) {
      state.position = position;
      // state.position = Position(longitude: 110.19707143306732, latitude: 25.264493823051453, timestamp: DateTime.now(), accuracy: 0, altitude: 0.0, altitudeAccuracy: 0.0, heading: 0.0, headingAccuracy: 0.0, speed: 0.0, speedAccuracy: 0.0);
    } else {
      state.position = await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.medium,
        timeLimit: const Duration(seconds: 10),
      );
    }
    logger2.d('current location '
        '${state.position.latitude.toStringAsFixed(5)} ${state.position.longitude.toStringAsFixed(5)} '
        'use ${DateTime.now().millisecondsSinceEpoch - start.millisecondsSinceEpoch} ms');
  }

  @override
  String getRemotePath() {
    return UTEPedConst.pathWeather;
  }

  @override
  String getStoragePath() {
    return UTEPedConst.pathWeather;
  }

  @override
  void initState() {
    // fromCacheOrRemoteLoad();
  }

  /// 清除之前保存的数据
  _clearCache() async {
    await Future.delayed(Duration.zero, () {
      getKeys(global: true).forEach((key) {
        if (key.startsWith('weather_')) {
          remove(key, global: true);
        }
      });
    });
  }

  Future<bool> fromCacheOrRemoteLoad() async {
    String futureWeatherKey = read(HomeConst.weatherFutureCacheKey, defaultValue: '', global: true);
    String weather24Key = read(HomeConst.weather24CacheKey, defaultValue: '', global: true);
    String todayWeatherKey = read(HomeConst.weatherTodayCacheKey, defaultValue: '', global: true);
    if (futureWeatherKey.isNotEmpty && weather24Key.isNotEmpty && todayWeatherKey.isNotEmpty) {
      int ts = int.parse(futureWeatherKey.split('_')[4]);
      int nowTs = DateTime.now().millisecondsSinceEpoch;
      if (nowTs - ts > 1800000) {
        await _clearCache();
        return loadWeatherData();
      } else {
        await getCurrentLocation();
        _initTodayWeather(read(todayWeatherKey, defaultValue: {}, global: true));
        _initFutureWeatherList(read(futureWeatherKey, defaultValue: [], global: true));
        _init24HWeatherList(read(weather24Key, defaultValue: {}, global: true));
        _doSyncWeatherData();
        return Future.delayed(const Duration(milliseconds: 500), () => true);
      }
    } else {
      return loadWeatherData();
    }
  }

  onSyncFutureWeatherSuccess(bool success) {
    if (success) {
      Map map = {
        // 'cityName': state.city.value.name,
        'currentTemperature': state.currentWeather.tempMin!,
        'unit': state.todayWeather.tempUnit,
        'lowest': state.todayWeather.tempMin!,
        'highest': state.todayWeather.tempMax!,
        'hum': state.currentWeather.humidity!.toInt(),
        'uv': int.parse(state.currentWeather.uvIndex ?? '0'),
        'publishTime': DateTool.getTimeInSeconds(DateTime.now(), truncateHms: false),
        'phenomenon': WeatherType.getType(state.todayWeather.weatherDesc!),
        'pm25': 0,
      };
      bRemoteReq(methodSetTodayWeather, {'map': map});
    }
  }

  @override
  void onRemoteEvent(event, method) {
    if (method == UTEPedConst.methodSetFutureWeather) {
      if (state.dailyWeathers.isNotEmpty) {
        _doSyncWeatherData();
      } else {
        loadWeatherDataForSync();
      }
    }
  }

  _doSyncWeatherData() async {
    bool isConnected = await UTEPedDevice.instance.isConnected();
    if (isConnected) {
      List dailyWeatherList = state.dailyWeathers.map((e) => e.toMap()).toList();
      List hourlyWeatherList = state.weather24h.map((e) => e.toMap()).toList();
      FutureWeatherModel fwm = FutureWeatherModel(
        weatherUpdateTimeStamp: DateTool.getTimeInSeconds(DateTime.now()),
        hourlyWeatherList,
        dailyWeatherList,
      );
      bRemoteReq(UTEPedConst.methodSetFutureWeather, {'map': fwm.toMap()}).then((value) => onSyncFutureWeatherSuccess(value));
    }
  }

  String getToken() {
    return read(NetConst.keyUserToken, defaultValue: '', global: true);
  }

  /// 获取预测的天气预报数据
  Future<void> getFutureWeather() async {
    var params = {
      "dayNum": 8,
      "lat": double.parse(state.position.latitude.toStringAsFixed(5)),
      "lon": double.parse(state.position.longitude.toStringAsFixed(5)),
    };
    var resp = await Dio().post(state.futureWeatherUrl,
        options: Options(
          headers: {
            // 'token': getToken(),
            'x-app-id': 'gofit',
          },
          contentType: Headers.jsonContentType,
        ),
        data: params);

    if (resp.statusCode == HttpStatus.ok) {
      Map map = jsonDecode(resp.toString());
      saveToCache(HomeConst.weatherFutureCacheKey, map);
      _initFutureWeatherList(map);
    }
  }

  /// 获取当天的天气预报数据
  Future<void> getTodayWeather() async {
    var resp = await Dio().post(
      state.todayWeatherUrl,
      options: Options(
        headers: {
          // 'token': getToken(),
          'x-app-id': 'gofit',
        },
        contentType: Headers.jsonContentType,
      ),
      data: {
        "lat": double.parse(state.position.latitude.toStringAsFixed(5)),
        "lon": double.parse(state.position.longitude.toStringAsFixed(5)),
      },
    );

    if (resp.statusCode == HttpStatus.ok) {
      Map map = jsonDecode(resp.toString());
      saveToCache(HomeConst.weatherTodayCacheKey, map);
      _initTodayWeather(jsonDecode(resp.toString()));
    }
  }

  /// 获取24的天气预报数据
  Future<void> get24HourWeather() async {
    var params = {
      "day": DateTool.getYMDWithDash(DateTime.now()),
      "lat": double.parse(state.position.latitude.toStringAsFixed(5)),
      "lon": double.parse(state.position.longitude.toStringAsFixed(5)),
    };
    var resp = await Dio().post(
      state.hour24WeatherUrl,
      options: Options(
        headers: {
          // 'token': getToken(),
          'x-app-id': 'gofit',
        },
        contentType: Headers.jsonContentType,
      ),
      data: params,
    );

    if (resp.statusCode == HttpStatus.ok) {
      Map map = jsonDecode(resp.toString());
      saveToCache(HomeConst.weather24CacheKey, map);
      _init24HWeatherList(map);
    }
  }
}
